package com.primeton.gateway.plugin;

import com.auth0.jwt.JWT;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.primeton.gateway.plugin.entity.AppLog;
import com.primeton.gateway.plugin.entity.CapUser;
import com.primeton.gateway.plugin.mqueue.productor.KafkaSender;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.HttpCookie;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.net.URI;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Calendar;

/**
 * @author: YunTao.Li
 * @create: 2020-04-01 16:22
 * @description:
 **/
@Component
public class RequestRecorderGlobalFilter implements GlobalFilter, Ordered {

    private Logger logger = LoggerFactory.getLogger(RequestRecorderGlobalFilter.class);

    @Autowired
    public KafkaSender sender;

    private Gson gson = new GsonBuilder().create();

    /**
     * 过滤器,请求在这里会被拦截
     *
     * @param exchange :
     * @param chain    :
     * @return : reactor.core.publisher.Mono<java.lang.Void>
     * @author : YunTao.Li
     * @date : 2020/4/2 2020/4/2
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest originalRequest = exchange.getRequest();
        //只记录http的请求
//        String scheme = originalRequestUrl.getScheme();
//        if ((!"http".equals(scheme) && !"https".equals(scheme))) {
//            return chain.filter(exchange);
//        }
        /**
         *  step1 : 初始化 记录对象
         * */
        AppLog applog = new AppLog();
        String callid = originalRequest.getId();
        String method = originalRequest.getMethodValue();
        String path = originalRequest.getPath().toString();
        String url = originalRequest.getURI().toString();
        applog.setRequestId(callid);
        applog.setMethod(method);
        applog.setPath(path);
        applog.setUrl(url);
        applog.setDateTime(Calendar.getInstance().getTime());

        //这是我要打印的log-StringBuilder
        StringBuilder logbuilder = new StringBuilder();
        logbuilder.append("==id:" + originalRequest.getId() + "==url:" + url + "===method:" + originalRequest.getMethod() + "==method value:" + originalRequest.getMethodValue() + "==path:" + originalRequest.getPath() + "==body:" + originalRequest.getBody() + "==================================================");

        /**
         *  step2 : 正常请求与请求返回
         * */
        RecorderServerHttpResponseDecorator response = new RecorderServerHttpResponseDecorator(exchange.getResponse(), applog);
        RecorderServerHttpRequestDecorator recorderServerHttpRequestDecorator = new RecorderServerHttpRequestDecorator(exchange.getRequest());

        /**
         *  step3 : 过滤拦截器
         * */
        ServerWebExchange ex = exchange.mutate()
                .request(recorderServerHttpRequestDecorator)
                .response(response)
                .build();

        return recorderOriginalRequest(logbuilder, ex, applog)
                .then(chain.filter(ex))
                .then();
    }

    /**
     * 原始请求
     *
     * @param logBuffer :
     * @param exchange  :
     * @param applog    :
     * @return : reactor.core.publisher.Mono<java.lang.Void>
     * @author : YunTao.Li
     * @date : 2020/4/2 2020/4/2
     */
    private Mono<Void> recorderOriginalRequest(StringBuilder logBuffer, ServerWebExchange exchange, AppLog applog) {
        logBuffer.append(System.currentTimeMillis())
                .append("------------");
        ServerHttpRequest request = exchange.getRequest();
        Mono<Void> result = recorderRequest(request, logBuffer.append("\n原始请求：\n"), applog);
        try {
//            appLogMapper.insert(applog);
            //TODO:send

            sender.sendMessage("apicall", applog.getId(), gson.toJson(applog));
        } catch (Exception e) {
            e.printStackTrace();
            logger.error("保存请求参数出现错误, e->{}", e.getMessage());
        }
        return result;
    }


    /**
     * 原始请求结果
     *
     * @param request   :
     * @param logBuffer :
     * @param applog    :
     * @return : reactor.core.publisher.Mono<java.lang.Void>
     * @author : YunTao.Li
     * @date : 2020/4/2 2020/4/2
     */
    private Mono<Void> recorderRequest(ServerHttpRequest request, StringBuilder logBuffer, AppLog applog) {
        URI uri = request.getURI();
        HttpMethod method = request.getMethod();
        HttpHeaders headers = request.getHeaders();

        String token = "";
        String syscode = "";
        String cofuname = "";
        try {
            token = headers.get("Authorization").toString();
            request.getCookies().get("sysCode");
            for (HttpCookie cookie : request.getCookies().get("sysCode")) {
                if (("sysCode").equals(cookie.getName())) {
                    syscode = cookie.getValue();
                }
            }
            DecodedJWT jwt = JWT.decode(token);
            String jwtvalue = jwt.getSubject();
            CapUser user = gson.fromJson(jwtvalue, CapUser.class);
            cofuname = user.getName();
            logger.error("===============user:" + user);
        } catch (Throwable e) {
            logger.error("获取token或syscode失败");
        }

        applog.setToken(token);
        applog.setSystemCode(syscode);
        applog.setCofUname(cofuname);

        /**
         *  记录请求头信息
         * */
        logBuffer.append(method.toString()).append(' ').append(uri.toString()).append('\n');
        logBuffer.append("------------请求头------------\n");
        headers.forEach((name, values) -> {
            values.forEach(value -> {
                logBuffer.append(name).append(":").append(value).append('\n');
            });
        });

        /**
         * 记录body信息
         * */
        Charset bodyCharset = null;
        if (hasBody(method)) {
            long length = headers.getContentLength();
            if (length <= 0) {
                logBuffer.append("------------无body------------\n");
            } else {
                logBuffer.append("------------body 长度:").append(length).append(" contentType:");
                MediaType contentType = headers.getContentType();
                if (contentType == null) {
                    logBuffer.append("null，不记录body------------\n");
                } else if (!shouldRecordBody(contentType)) {
                    logBuffer.append(contentType.toString()).append("，不记录body------------\n");
                } else {
                    bodyCharset = getMediaTypeCharset(contentType);
                    logBuffer.append(contentType.toString()).append("------------\n");
                }
            }
        }

//        applog.setLogBuffer(logBuffer.toString());
        if (bodyCharset != null) {
            return doRecordReqBody(logBuffer, request.getBody(), bodyCharset, applog)
                    .then(Mono.defer(() -> {
                        logBuffer.append("\n------------ end ------------\n\n");
                        return Mono.empty();
                    }));
        } else {
            logBuffer.append("------------ end ------------\n\n");

        }
        return Mono.empty();
    }

//    //日志输出返回值
//    private Mono<Void> printLog(StringBuilder logBuilder, ServerHttpResponse response) {
//        HttpStatus statusCode = response.getStatusCode();
//        assert statusCode != null;
//        logBuilder.append("响应：").append(statusCode.value()).append(" ").append(statusCode.getReasonPhrase()).append('\n');
//        HttpHeaders headers = response.getHeaders();
//        logBuilder.append("------------响应头------------\n");
//        headers.forEach((name, values) -> {
//            values.forEach(value -> {
//                logBuilder.append(name).append(":").append(value).append('\n');
//            });
//        });
//        logBuilder.append("\n------------ end at ")
//                .append(System.currentTimeMillis())
//                .append("------------\n\n");
//        logger.info(logBuilder.toString());
//        return Mono.empty();
//    }

    //
    @Override
    public int getOrder() {
        //在GatewayFilter之前执行
        return -1;
    }

    private boolean hasBody(HttpMethod method) {
        //只记录这3种谓词的body
//        if (method == HttpMethod.POST || method == HttpMethod.PUT || method == HttpMethod.PATCH)
        return true;

//        return false;
    }

    /**
     * 记录简单的常见的文本类型的request的body和response的body
     *
     * @param contentType :
     * @return : boolean
     * @author : YunTao.Li
     * @date : 2020/4/2 2020/4/2
     */
    private boolean shouldRecordBody(MediaType contentType) {
//        String type = contentType.getType();
//        String subType = contentType.getSubtype();
//
//        if ("application".equals(type)) {
//            return "json".equals(subType) || "x-www-form-urlencoded".equals(subType) || "xml".equals(subType) || "atom+xml".equals(subType) || "rss+xml".equals(subType);
//        } else if ("text".equals(type)) {
//            return true;
//        }
//        //暂时不记录form
        //永远记录body
        return true;
    }

    /**
     * 获取请求的参数
     *
     * @param logBuffer :
     * @param body      :
     * @param charset   :
     * @param applog    :
     * @return : reactor.core.publisher.Mono<java.lang.Void>
     * @author : YunTao.Li
     * @date : 2020/4/2 2020/4/2
     */
    private Mono<Void> doRecordReqBody(StringBuilder logBuffer, Flux<DataBuffer> body, Charset charset, AppLog applog) {
        return DataBufferUtils.join(body).doOnNext(buffer -> {
            CharBuffer charBuffer = charset.decode(buffer.asByteBuffer());
            //记录我实体的请求体
            logger.info("=============param:" + charBuffer.toString());
            logBuffer.append(charBuffer.toString());
            DataBufferUtils.release(buffer);
        }).then();
    }

    private Charset getMediaTypeCharset(@Nullable MediaType mediaType) {
        if (mediaType != null && mediaType.getCharset() != null) {
            return mediaType.getCharset();
        } else {
            return StandardCharsets.UTF_8;
        }
    }
}
